In a client-server environment, clients often cache data that the server owns and manages. The client stores a copy of data from the server locally on the client (e.g., in random access memory (RAM), a page file, a local hard disk, or a flash memory device). The client can access and modify the cached data locally without communicating across a network or other communication channel for accessing the data remotely at the server. Because network access is much slower than local access, accessing data locally is more efficient and reduces the burden upon the server so that the server can handle more requests. Local caching may have many benefits. For example, local caching allows the client to combine multiple write operations on the same region of a file into one write operation across the network. In addition, for read operations the client does not need to request data from the server for each operation if applications read the data multiple times. Caching improves the response time of applications because the applications do not wait for the client to send data across the network to the server upon every request.
One consideration when a server allows multiple clients to access and cache data is ensuring that the clients do not perform conflicting actions on locally cached copies of the data. For example, if one client writes new data to its local cache and a second client reads data from its local cache, the second client will be unaware of the first client's changes without mechanisms on the server to ensure cache coherency. Some network protocols provide a cache coherency mechanism whereby clients inform the server of the manner in which the client will use and cache the data (client intent). For example, a client may cache only reads, only writes, both reads and writes, and so forth. Clients may also have the ability to cache file handles so that applications can reuse the same handle for later requests to open the same file.
In SMB 2, the mechanism for performing this type of cache coherency is called an opportunistic lock (oplock). A client opportunistically requests the type of access that it wants to a file and the server conditionally grants or denies access. Even if the server grants access, the server may later break the lock (called a break) by sending the client a notification that another client has requested conflicting access. In the example described above, if the first client obtained a write-based oplock to a file, the second client's request to obtain a read-based oplock would fail, and the second client would thereby know that the locally cached data could be stale so that the second client will retrieve the newest data from the server. Using knowledge of the clients that are accessing a particular file and the intent expressed by each client, the server can manage access to the file so that each client stays consistent. For example, the server may temporarily cause a new client to wait for an existing client (who is caching server data) to flush cached data (e.g., sending cached, modified data back from the client to the server) and cause the data on the server to be consistent, before allowing the new client to access the data.
Unfortunately, the existing oplock semantics were designed 20 years ago when application behavior was significantly more simple and predictable. On modern operating systems, developers build applications over multiple layers of abstraction, and often end up performing redundant file system operations. For example, several different components within the same application may open the same file, each with different intentions for using the file (e.g., some reading and some writing). The cost of these redundant operations may be acceptable when the file is stored locally, but when an application accesses the file over a network the cost can quickly add up, resulting in unresponsiveness observed by an end user or network chattiness observed by a network administrator. The existing oplock model allows clients to cache data under some circumstances, but modern applications operate outside of the caching circumstances anticipated by the SMB 2 designers in many cases, with the result that the client is often unable to locally cache data or file handles.
In addition, modern computing systems run many more applications simultaneously than in the past, and several applications may attempt to access the same remote file at the same time. SMB 2 and other protocols typically treat each access request as coming from a separate client, even if the requests come from different applications on the same client. When multiple applications are running on the same client, the likelihood is high that a user is using the applications to perform a single task that involves a particular data file. As an example, a shell (e.g., computer user interface) may be trying to query icon attributes at the same time that a document application is trying to open and save a document. This can break an oplock related to the document so that the client determines the cache to no longer be valid. This makes read/write operations slower because they have to go across the network. As another example, the shell may try to render a preview of a document in a smart icon and at the same time, a search indexer may try to index the content. When two applications on the same client decide to access the file simultaneously, the server could revoke the ability of that client to cache data. These situations further reduce the opportunity for caching using existing caching semantics.
SMB2 oplocks have several limitations. First, oplocks are tied to an open file handle. When a client opens a file, the client requests a particular oplock and receives the lock along with a handle to the file from the server. This means that a client can maintain an oplock only if it has an open handle to a file. Second, the protocol does not allow clients to cache writes and open handles if there is more than one open handle to the file. As noted above, most modern applications open multiple handles to the same file, effectively resulting in a loss of write caching and handle caching. For the same reason, multiple clients cannot cache open file handles. Finally, if multiple applications are read-caching data, the client cannot maintain the read-cache after the application that initially read the data closes the handle to the file. This is because as noted above the oplock is associated with the handle with which it was opened.